﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Threading;
using System.Drawing.Drawing2D;
using WindowsFormsApplication2.Properties;

namespace WindowsFormsApplication2
{
    public partial class KiviatPlot : Form
    {
        private List<Measurement> m_Measurements = new List<Measurement>();

        public KiviatPlot()
        {
            InitializeComponent();
            
            Measurement documentation = new Measurement("Documentation", 5f, 20f, 9.2f);
            Measurement comments = new Measurement("Comments", 5f, 40f, 3.8f);
            Measurement avgComplexity = new Measurement("Average Complexity", 2f, 4f, 1.96f);
            Measurement avgDepth = new Measurement("Average Depth", 1.5f, 3f, 2.1f);
            Measurement maxDepth = new Measurement("Maximum Depth", 3f, 6f, 11f);
            Measurement maxComplexity = new Measurement("Maximum Complexity", 2f, 8f, 227f);
            Measurement methodsPerClass = new Measurement("Methods per Class", 2f, 22f, 7.52f);

            m_Measurements.AddRange(
                new Measurement[] 
                {
                    documentation, 
                    comments, 
                    avgComplexity, 
                    avgDepth, 
                    maxDepth, 
                    maxComplexity,
                    methodsPerClass,


                    //documentation, 
                    //comments, 
                    //avgComplexity, 
                    //avgDepth, 
                    //maxDepth, 
                    //maxComplexity,
                    //methodsPerClass,
                    //documentation, 
                    //comments, 
                    //avgComplexity, 
                    //avgDepth, 
                    //maxDepth, 
                    //maxComplexity,
                    //methodsPerClass,
                    //documentation, 
                    //comments, 
                    //avgComplexity, 
                    //avgDepth, 
                    //maxDepth, 
                    //maxComplexity,
                    //methodsPerClass,
                    //documentation, 
                    //comments, 
                    //avgComplexity, 
                    //avgDepth, 
                    //maxDepth, 
                    //maxComplexity,
                    //methodsPerClass,
                    //documentation, 
                    //comments, 
                    //avgComplexity, 
                    //avgDepth, 
                    //maxDepth, 
                    //maxComplexity,
                    //methodsPerClass,
                    //documentation, 
                    //comments, 
                    //avgComplexity, 
                    //avgDepth, 
                    //maxDepth, 
                    //maxComplexity,
                    //methodsPerClass,
                    //documentation, 
                    //comments, 
                    //avgComplexity, 
                    //avgDepth, 
                    //maxDepth, 
                    //maxComplexity,
                    //methodsPerClass,

                    


                });
        }

        private void OnCreatePlotClicked(object sender, EventArgs e)
        {
            CreatePlot();
        }

        int dimensionX;
        int dimensionY;

        Bitmap bitmap;
        Bitmap flag;  
        Graphics plot;

        List<Tuple<float, PointF, PointF>> lines = new List<Tuple<float, PointF, PointF>>();

        private void CreatePlot()
        {
            dimensionX = this.m_pic_Plot.Width;
            dimensionY = this.m_pic_Plot.Height;
            int population = this.m_Measurements.Count;
            List<Tuple<Measurement, float, float>> scatters = new List<Tuple<Measurement, float, float>>();

            float lowThreshold = 30f;
            float highThreshold = 60f;

            double circleDegs = 360d;

            bitmap = new Bitmap(dimensionX, dimensionY);
            flag = Resources.scatter;
            plot = Graphics.FromImage(bitmap);
            plot.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            plot.Clear(Color.White);

            float originX = dimensionX / 2;
            float originY = dimensionY / 2;

            double radius = this.m_pic_Plot.Height / 2;
            double stepAngle = 360d / (double)population;//20d;
            double currentAngle = ((double)0d).ToRad();
            double offset = 5d;

            Font legendFont = new Font("Tahoma", 7f);

            for (int itemIdx = 0; itemIdx < population; itemIdx++)
            {
                float stepX = (float)(radius * Math.Cos(currentAngle));
                float stepY = -(float)(radius * Math.Sin(currentAngle));

                float currentX = originX + stepX;
                float currentY = originY + stepY;

                Measurement item = m_Measurements[itemIdx];

                float normalizedValue = (item.Value * highThreshold) / item.MaxRange > 100 ? 100 : (item.Value * highThreshold) / item.MaxRange;
                float normalizedRadius = (float)radius * normalizedValue / 100f;
                float scatterPosX = originX + (float)(normalizedRadius * Math.Cos(currentAngle));
                float scatterPosY = originY - (float)(normalizedRadius * Math.Sin(currentAngle));

                plot.DrawString(item.Value.ToString() + ", " + normalizedValue.ToString(), legendFont, Brushes.Black, scatterPosX , scatterPosY);
                //plot.DrawImage(flag, new Point((int)scatterPosX, (int)scatterPosY));
                scatters.Add(new Tuple<Measurement, float, float>(item, scatterPosX, scatterPosY));

                double quadrant = System.Math.Ceiling((currentAngle.ToDeg() / 90d));

                float textPosX = 0f;
                float textPosY = 0f;

                textPosX = (float)(currentX + offset * 3);
                textPosY = (float)(currentY - offset * 2);

                string legend = String.Format("{0}:[{1}-{2}]",
                    item.Name, 
                    item.MinRange,
                    item.MaxRange);

                if (System.Math.Ceiling(currentAngle.ToDeg()) <= 90d || 
                    System.Math.Ceiling(currentAngle.ToDeg()) >= 270d )
                {
                    textPosX = (float)(currentX + offset * 3);
                    textPosY = (float)(currentY - offset * 2);
                }
                else
                {
                    SizeF legendSize = plot.MeasureString(legend, legendFont);
                    
                    textPosX = (float)(currentX - (offset) - legendSize.Width );
                    textPosY = (float)(currentY - offset);
                }

                plot.DrawString(
                    legend,
                    legendFont, Brushes.Black,
                    textPosX,
                    textPosY);

                plot.DrawLine(
                    new Pen(Brushes.Blue), 
                    new Point((int)currentX, (int)currentY), 
                    new Point((int)originX, (int)originY));

                this.m_pic_Plot.Image = bitmap;
                //Thread.Sleep(100);

                if (currentAngle > circleDegs.ToRad())
                {
                    currentAngle = 0d;
                    radius += radius * 2 / 3;
                }
                else
                {
                    currentAngle += stepAngle.ToRad();
                }
            }

            DrawLines(scatters);

        }

        private void Prepare()
        {

        }

        private void DrawLines(List<Tuple<Measurement, float, float>> scatters)
        {
            int scatterIdx = 0;

            while (scatterIdx < scatters.Count)
            {
                PointF lower; int lowerIdx;
                PointF higher; int higherIdx;

                if (scatterIdx + 1 < scatters.Count)
                {
                    ++scatterIdx;

                    lowerIdx = scatterIdx - 1;
                    higherIdx = scatterIdx;
                }
                else
                {
                    lowerIdx = scatterIdx;
                    higherIdx = 0;
                }

                lower = new PointF(scatters[lowerIdx].Item2, scatters[lowerIdx].Item3);
                higher = new PointF(scatters[higherIdx].Item2, scatters[higherIdx].Item3);

                float lineDirection = 
                    (scatters[higherIdx].Item3 - scatters[lowerIdx].Item3) / 
                    (scatters[higherIdx].Item2 - scatters[lowerIdx].Item2);

                plot.DrawLine(
                    new Pen(Brushes.Red),
                    lower,
                    higher
                    );

                lines.Add(new Tuple<float, PointF, PointF>(lineDirection, lower, higher));

                if (higherIdx == 0)
                {
                    break;
                }
            }
        }

        private void KiviatPlot_SizeChanged(object sender, EventArgs e)
        {
            this.CreatePlot();
        }

        private float LineFunction(float lineDirection, PointF higherPoint, float currentX)
        {
            return lineDirection * (currentX - higherPoint.X) + higherPoint.Y;
        }

        private float SimpsonCompositeIntegration(List<Tuple<float, PointF, PointF>> lines)
        {
            float sum = 0f;
            string message = String.Empty;

            foreach (Tuple<float, PointF, PointF> line in lines)
            {
                float partialSum = 0f;
                float a = line.Item2.X;
                float b = line.Item3.X;
                int stepNumber = 15;
                float stepSize = (b - a) / stepNumber;

                for (int stepIdx = 0; stepIdx < stepNumber; stepIdx = stepIdx + 2)
                {
                    partialSum += (Function(line, a + stepIdx * stepSize)
                        + 4 * Function(line, a + (stepIdx + 1) * stepSize)
                        + Function(line, a + (stepIdx + 2) * stepSize)) * stepSize / 3;
                }

                sum += partialSum;
                message += partialSum.ToString() + System.Environment.NewLine;

            }

            MessageBox.Show(message);
            return sum;
        }

        private float Function(Tuple<float, PointF, PointF> line, float x)
        {
            float y = line.Item1 * (x - line.Item3.X) + line.Item3.Y;
            return y;
        }

        private void OnAreaClicked(object sender, EventArgs e)
        {
            MessageBox.Show(SimpsonCompositeIntegration(lines).ToString());
        }
    }
}
